import service
import uthread
import stackless
import locks
import collections
import entities
import log
import sys
import entityCommon
import util

class Entity():
    __guid__ = 'entities.Entity'

    def __init__(self, scene, entityID):
        self.scene = scene
        self.entityID = entityID
        self.components = {}
        self.state = const.zentity.ENTITY_STATE_UNINITIALIZED
        self.entityLock = uthread.CriticalSection()



    def AddComponent(self, name, component):
        if (name in self.components):
            msg = ('Adding a component:',
             name,
             "that's already on the a entity:",
             self.entityID,
             ' Old component will be replaced')
            self.scene.service.LogError('Adding a component:', name, "that's already on the a entity:", self.entityID, ' Old component will be replaced')
            if ('logging' in self.components):
                self.logging.Log(msg)
        if (component is not None):
            self.components[name] = component
        else:
            log.LogWarn('Unable to add component', name, 'to entity', self.entityID)



    def HasComponent(self, name):
        return (name in self.components)



    def GetComponent(self, name):
        return self.components.get(name, None)



    def __getattr__(self, attr):
        if self.components.has_key(attr):
            return self.components[attr]
        if self.__dict__.has_key(attr):
            return self.__dict__[attr]
        raise AttributeError(attr)



    def Log(self, str):
        logging = self.components.get('logging', None)
        if logging:
            logging.Log(str)




class EntitySceneState():
    UNINITIALIZED = 0
    LOADING = 1
    READY = 2
    UNLOADING = 3


class EventQueue(object):

    def __init__(self, emptyEvent = None):
        self._task = None
        self._queue = collections.deque()
        self._emptyEvent = emptyEvent



    def AddEvent(self, event, *args):
        semaphore = locks.Event(('event_%s' % event))
        self._queue.append((event,
         args,
         semaphore))
        return semaphore



    def StartEvents(self):
        if (self._task is None):
            self._task = uthread.new(self._PumpEvent)
            self._task.context = 'EventQueue update'



    def GetLastSemaphore(self):
        try:
            (event, args, semaphore,) = self._queue[-1]
            return semaphore
        except:
            log.LogError('Missing semaphore')
            return 



    def _ProcessEvent(self, semaphore, event, *args):
        event(*args)
        semaphore.set()



    def _PumpEvent(self):
        while (len(self._queue) > 0):
            (event, args, semaphore,) = self._queue[0]
            self._ProcessEvent(semaphore, event, *args)
            self._queue.popleft()

        self._task = None
        if (self._emptyEvent is not None):
            self._emptyEvent()




class BaseEntityScene(object):
    __guid__ = 'entities.BaseEntityScene'
    __exportedcalls__ = {'ReceiveRemoteEntity': {'role': service.ROLE_SERVICE}}

    def __init__(self, broker, sceneID):
        self.state = EntitySceneState.UNINITIALIZED
        self.sceneID = sceneID
        self.entities = {}
        self.broker = broker
        self.eventQueue = EventQueue()
        self._entityList = {}



    def IsUnloading(self):
        return (self.state == EntitySceneState.UNLOADING)



    def _LoadScene(self, semaphore):
        self.broker.LogInfo('Asynch scene load started for scene', self.sceneID)
        if (semaphore is not None):
            self.broker.LogError('Scene load waiting for previous unload for scene', self.sceneID)
            semaphore.wait()
        self.state = EntitySceneState.LOADING
        self.broker.LogInfo('Scene load starting registration for scene', self.sceneID)
        self.broker._RegisterScene(self, self.loadSemaphore)
        self.broker.LogInfo('Asynch scene load finished for scene', self.sceneID)



    def _UnloadScene(self):
        self.broker.LogInfo('Asynch scene unload started for scene', self.sceneID)
        self.state = EntitySceneState.UNLOADING
        semaphores = []
        for entityID in self.entities.iterkeys():
            queue = self.broker._entityQueues.get(entityID, None)
            if (queue is not None):
                semaphore = queue.GetLastSemaphore()
                if (semaphore is not None):
                    semaphores.append(semaphore)

        for semaphore in semaphores:
            semaphore.wait()

        semaphores = []
        for entity in self.entities.itervalues():
            semaphores.append(self.UnregisterAndDestroyEntity(entity))

        for semaphore in semaphores:
            semaphore.wait()

        self.broker.LogInfo('Scene unload starting unregistration for scene', self.sceneID)
        self.broker._UnregisterScene(self)
        self.broker.LogInfo('Asynch scene unload finished for scene', self.sceneID)



    def LoadScene(self, scene):
        semaphore = None
        if (scene is not None):
            self.broker.LogError('Scene', self.sceneID, 'waiting for unload of the same scene')
            semaphore = scene.eventQueue.GetLastSemaphore()
            if (semaphore is None):
                self.broker.LogError('OMG predcessor scene has no semaphore ', self.sceneID)
        self.loadSemaphore = self.eventQueue.AddEvent(self._LoadScene, semaphore)
        self.eventQueue.StartEvents()
        return self.loadSemaphore



    def UnloadScene(self):
        semaphore = self.eventQueue.AddEvent(self._UnloadScene)
        self.eventQueue.StartEvents()
        return semaphore



    def CreateEntity(self, entity, initData = None):
        self._entityList[entity.entityID] = entity
        channel = self.broker._AddEvent(entity.entityID, self.broker._CreateEntity, entity, self.loadSemaphore, initData)
        self.broker._StartEvents(entity.entityID)
        return channel



    def RegisterEntity(self, entity, initData = None):
        channel = self.broker._AddEvent(entity.entityID, self.broker._RegisterEntity, entity, initData)
        self.broker._StartEvents(entity.entityID)
        return channel



    def UpdateEntity(self, entity, updateData = None):
        channel = self.broker._AddEvent(entity.entityID, self.broker._UpdateEntity, entity, updateData)
        self.broker._StartEvents(entity.entityID)
        return channel



    def UnregisterEntity(self, entity):
        channel = self.broker._AddEvent(entity.entityID, self.broker._UnregisterEntity, entity)
        self.broker._StartEvents(entity.entityID)
        return channel



    def DestroyEntity(self, entity):
        del self._entityList[entity.entityID]
        channel = self.broker._AddEvent(entity.entityID, self.broker._DestroyEntity, entity)
        self.broker._StartEvents(entity.entityID)
        return channel



    def CreateAndRegisterEntity(self, entity, initData = None):
        self._entityList[entity.entityID] = entity
        self.broker._AddEvent(entity.entityID, self.broker._CreateEntity, entity, self.loadSemaphore, initData)
        channel = self.broker._AddEvent(entity.entityID, self.broker._RegisterEntity, entity, initData)
        self.broker._StartEvents(entity.entityID)
        return channel



    def UnregisterAndDestroyEntity(self, entity):
        del self._entityList[entity.entityID]
        self.broker._AddEvent(entity.entityID, self.broker._UnregisterEntity, entity)
        channel = self.broker._AddEvent(entity.entityID, self.broker._DestroyEntity, entity)
        self.broker._StartEvents(entity.entityID)
        return channel



    def AddEntityToRegisteredList(self, entity):
        self.entities[entity.entityID] = entity



    def RemoveEntityFromRegisteredList(self, entity):
        del self.entities[entity.entityID]



    def GetAllEntityIds(self):
        return self.entities.keys()



    def ReceiveRemoteEntity(self, fromSceneID, entityID, packedState):
        return self.broker.ReceiveRemoteEntity(fromSceneID, entityID, self.sceneID, packedState)




class BaseEntityService(service.Service):
    __guid__ = 'svc.BaseEntityService'


